package com.zx;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.ConfigurableEnvironment;

//太阳
@EnableCaching
@SpringBootApplication
@EnableAutoConfiguration
public class DemoApplication {

//	第一部分进行SpringApplication的初始化模块，配置一些基本的环境变量、资源、构造器、监听器，
//	第二部分实现了应用具体的启动方案，包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块，
//	第三部分是自动化配置模块，该模块作为springboot自动配置核心。
	
	
//	启动类上常有@SpringBootApplication与@ImportResource注解(if need)
//		@SpringBootApplication包括三个注解，功能如下：
//		@EnableAutoConfiguration：SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置
//		@SpringBootConfiguration(内部为@Configuration)：被标注的类等于在spring的XML配置文件中(applicationContext.xml)，
//		装配所有bean事务，提供了一个spring的上下文环境
//		@ComponentScan：组件扫描，可自动发现和装配Bean，默认扫描SpringApplication的run方法里的Booter.class所在的包路径下文件
//		
//	@ImportResource 
//		@ImportResource("classpath:beans.xml") 引入指定的beans
	
	
//	run方法中去创建了一个SpringApplication实例，在该构造方法内，初始化initialize方法
//	执行SpringApplication的run方法()
//	1.创建了应用的监听器SpringApplicationRunListeners并开始监听
//	2.加载SpringBoot配置环境(ConfigurableEnvironment)，如果是通过web容器发布，会加载StandardEnvironment，其最终也是继承了ConfigurableEnvironment
//		Environment最终都实现了PropertyResolver接口，既@ConfigurationProperties获取获取配置文件中指定Key对应的value方法时
//	3.配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
//	4.配置ConfigurableApplicationContext 获取显式设置的应用上下文(applicationContextClass)，
//		如果不存在，再加载默认的环境配置（通过是否是web environment判断），
//		默认选择AnnotationConfigApplicationContext注解上下文（通过扫描所有注解类来加载bean），
//		最后通过BeanUtils实例化上下文对象
//	5.prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
//	6.接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键，
//		包括spring.factories的加载，bean的实例化等核心工作。
	
//	Springboot的启动，主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext)，并基于以上条件，
//	在容器中开始实例化我们需要的Bean，至此，通过SpringBoot启动的程序已经构造完成
	

//	SpringBoot自动配置模块
//	@EnableAutoConfiguration->AutoConfigurationImportSelector的selectImports方法
//	使用到了SpringFactoriesLoader，即Spring工厂加载器，该对象提供了loadFactoryNames方法，入参为factoryClass和classLoader，
//	方法会根据指定的classLoader，加载该类加器搜索路径下的指定文件，即spring.factories文件，
//	传入的工厂类为接口，而文件中对应的类则是接口的实现类，或最终作为实现类，文件中一般为一对多的类名集合，
//	获取到这些实现类的类名后，loadFactoryNames方法返回类名集合，方法调用方得到这些集合后，再通过反射获取这些类的类对象、构造方法，最终生成实例 
	
	
//	通过spring-boot的starter依赖方式依赖了我们需要的组件,如mybatis-spring-boot-starter、spring-boot-starter-web等组件
//		SpringApplication的run()方法中SpringFactoriesLoader收集classloader路径下的的META-INF文件中的spring.factories文件:
//		org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
//		org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
//		org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
//		org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
//		自动配置模块中，SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组，
//	返回的类全名通过反射被实例化，就形成了具体的工厂实例，工厂实例来生成组件具体需要的bean。
	
//	@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})这个注解的意思是：
//	当存在SqlSessionFactory.class, SqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,
//	否则不解析这一个配置类，
//	@CondtionalOnBean(DataSource.class):只处理已经被声明为bean的dataSource
//	@ConditionalOnMissingBean(MapperFactoryBean.class)只处理容器中不存在name指定的bean则创建bean注入，否则不执行
	
//	@EnableAutoConfiguration->AutoConfigurationImportSelector->SpringFactoriesLoader->spring.factories->最终调用各种starter的AutoConfiguration
//	根据AutoConfiguration上的@CondtionalOnXXX进行判断是否初始化Bean
	
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}
